#include "method/projection.h"
#include "model/scene.h"

#include <QDebug>

#define PI 3.141592654

Projection::~Projection() {}

Scene *PerspectiveProjection::apply(Scene *scene, const Vertex &observer, const Vertex &focus, float zoom, float roll) {

    Scene *newScene = new Scene(*scene);
    coord angleRadians = roll * PI / 180;

    Vertex j(sin(angleRadians), cos(angleRadians), 0);
    Vertex cpp = observer+(focus-observer) * zoom;
    Vertex n = (observer-focus).unit();
    Vertex v = (j-n*(n*j)).unit();
    Vertex u = v.vetorial(n);

    MatrixTransformation rt;
    rt.translation(-observer.x, -observer.y, -observer.z);

    coord r[4][4] {
        {u.x, u.y, u.z, 0},
        {v.x, v.y, v.z, 0},
        {n.x, n.y, n.z, 0},
        {  0,   0,   0, 1}
    };

    rt.multiply(r);

    coord p[4][4] {
        {  1,   0,   0,   0},
        {  0,   1,   0,   0},
        {  0,   0,   1,   0},
        {  0,   0, -((coord)1/(coord)fabs((rt[2][0]*cpp.x) + (rt[2][1]*cpp.y) + (rt[2][2]*cpp.z) + rt[2][3])),   0}
    };

    MatrixTransformation prt = rt;
    prt.multiply(p);

    for ( Object *object: newScene->getObjects() ) {
        Object *zIndex = new Object(*object);
        zIndex->apply(rt);
        object->apply(prt);

        object->resetLimits();
        for ( Vertex* vertex: object->vertexs ) {
            vertex->x /= vertex->w;
            vertex->y /= vertex->w;
            vertex->z = zIndex->vertexs[vertex->index]->z;
            vertex->w = 1;
            object->updateLimits(*vertex);
        }

        delete zIndex;
    }

    return newScene;
}

Scene *IsometrictProjection::apply(Scene *scene, const Vertex &, const Vertex &, float, float) {
    Object* o = scene->getGround();
    coord position = -(o->width()+o->height())*10;

    Vertex observer(position, position, position);
    Vertex focus = o->center();
    Vertex j(0, 1, 0);

    Vertex n = (observer-focus).unit();
    Vertex v = (j-n*(n*j)).unit();
    Vertex u = n.vetorial(v);

    MatrixTransformation rt;
    rt.translation(-observer.x, -observer.y, -observer.z);

    coord r[4][4] {
        {u.x, u.y, u.z, 0},
        {v.x, v.y, v.z, 0},
        {n.x, n.y, n.z, 0},
        {  0,   0,   0, 1}
    };

    rt.multiply(r);

    Scene *ns = new Scene(*scene);
    for ( Object *objects: ns->getObjects() ) {
        objects->apply(rt);
    }

    return ns;
    return scene;
}

